﻿//-----------------------------------------------------------------------
// <copyright file="Program.cs" company="Microsoft Corporation">
// Copyright  Microsoft Corporation.
// All rights reserved.
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
// </copyright>
// <author>Vivek</author>
// <email>visoni@microsoft.com</email>
// <date>09-March-2011</date>
// <summary>Deploy Sandbox InfoPath Form</summary>
//-----------------------------------------------------------------------

namespace DeploySandboxInfoPathForm
{
    using System;
    using System.IO;
    using System.Linq;
    using DeploySandboxInfoPathForm.FormsService;
    using Microsoft.SharePoint.Client;

    /// <summary>
    /// Sandbox InfoPath solution deployment class.
    /// </summary>
    public static class Program
    {
        #region Class level variables

        /// <summary>
        /// Formsservice proxy object.
        /// </summary>
        private static FormsService.FormsServicesWebService formsService = null;

        /// <summary>
        /// The URL of the SharePoint site.
        /// </summary>
        private static string siteURL = string.Empty;

        /// <summary>
        /// Relative URL of the forms library.
        /// </summary>
        private static string formLibURL = string.Empty;

        /// <summary>
        /// Local path of the form template. 
        /// </summary>
        private static string xsnPath = string.Empty;

        /// <summary>
        /// New file name for the form XSN.
        /// </summary>
        private static string newFileName = string.Empty;

        /// <summary>
        /// Target SharePoint site url.
        /// </summary>
        private static string rootSPSiteURL = string.Empty;

        #endregion

        /// <summary>
        /// Main method to run the operations.
        /// </summary>
        /// <param name="args">Command line arguments.</param>
        public static void Main(string[] args)
        {
            bool silentmode = false;

            if (args != null && args.Length > 2)
            {
                siteURL = args[0].Trim();
                formLibURL = args[1].Trim();
                xsnPath = args[2].Trim();
                if (args.Length > 2)
                {
                    newFileName = args[3].Trim();
                }

                silentmode = true;
            }
            else
            {
                LogToConsole("Please provide the SharePoint site url.");
                siteURL = Console.ReadLine().Trim();

                LogToConsole("Please provide the target forms library name or relative URL.");
                formLibURL = Console.ReadLine().Trim();

                LogToConsole("Please provide the path of the local InfoPath XSN file you wish to upload.");
                xsnPath = Console.ReadLine().Trim();

                LogToConsole("Optional: New file name. (hit enter to escape entering this parameter.)");
                newFileName = Console.ReadLine().Trim();
            }

            if (!string.IsNullOrEmpty(siteURL))
            {
                if (!string.IsNullOrEmpty(formLibURL))
                {
                    if (!string.IsNullOrEmpty(xsnPath))
                    {
                        if (!string.IsNullOrEmpty(newFileName) && !newFileName.EndsWith(".xsn", StringComparison.OrdinalIgnoreCase))
                        {
                            LogToConsole("The new file name specified does not appears to be a valid. The file system name of the XSN file would be used as the default name.", true);
                            newFileName = string.Empty;
                        }

                        rootSPSiteURL = GetSiteURL(siteURL);
                        string xsnUrl = UploadFormToLibrary();
                        if (!string.IsNullOrEmpty(xsnUrl))
                        {
                            ConvertFormToBrowserEnabled(xsnUrl);
                        }
                        else
                        {
                            LogToConsole("Could not complete the operation...", true);
                        }
                    }
                    else
                    {
                        LogToConsole("Please the path of the local InfoPath XSN file you wish to upload and rerun the application.", true);
                    }
                }
                else
                {
                    LogToConsole("Please provide the target forms library name or relative URL and rerun the application.", true);
                }
            }
            else
            {
                LogToConsole("Please provide the SharePoint site url and rerun the application.", true);
            }

            if (!silentmode)
            {
                LogToConsole(Environment.NewLine);
                LogToConsole("Press any key to exit.");
                Console.ReadKey();
            }
            else
            {
                LogToConsole(Environment.NewLine, false);
            }
        }

        #region Class level methods

        /// <summary>
        /// Uploads a XSN form to the SharePoint forms library.
        /// </summary>
        /// <returns>Absolute URL of the form template.</returns>
        private static string UploadFormToLibrary()
        {
            string xsnURL = string.Empty;
            ClientContext context = null;
            try
            {
                context = new ClientContext(siteURL);
                if (context != null)
                {
                    LogToConsole("Checking current user permissions...", true);
                    context.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
                    Web web = context.Web;
                    ClientResult<bool> result = web.DoesUserHavePermissions(GetPermissionMask());
                    context.Load(context.Site, rootsite => rootsite.Url);
                    context.ExecuteQuery();
                    if (result.Value)
                    {
                        string fileName = string.Empty;
                        byte[] filecontents = ConvertFormToByteArray(xsnPath, out fileName);
                        if (filecontents != null && filecontents.Length > 0)
                        {
                            LogToConsole("Uploading the local InfoPath form template to specified SharePoint library...", true);
                            Folder docLibFolder = web.GetFolderByServerRelativeUrl(formLibURL.TrimStart("/".ToCharArray()));
                            if (docLibFolder != null)
                            {
                                if (!string.IsNullOrEmpty(newFileName))
                                {
                                    fileName = newFileName;
                                }

                                FileCreationInformation fileCreation = new FileCreationInformation();
                                fileCreation.Url = fileName;
                                fileCreation.Content = filecontents;
                                fileCreation.Overwrite = true;
                                Microsoft.SharePoint.Client.File form = docLibFolder.Files.Add(fileCreation);
                                context.Load(form);
                                context.ExecuteQuery();
                                if (form != null)
                                {
                                    xsnURL = siteURL.TrimEnd("/".ToCharArray()) + "/" + formLibURL.TrimEnd("/".ToCharArray()).TrimStart("/".ToCharArray()) + "/" + form.Name;
                                    LogToConsole(string.Format("Successfully uploaded the form template to {0}...", xsnURL), true);
                                }
                            }
                        }
                    }
                    else
                    {
                        LogToConsole(string.Format("You dont have the necessary permission to perform this operation."), true);
                    }
                }
            }
            catch (Exception ex)
            {
                LogToConsole("Error occured in uploading InfoPath form template...", true);
                Console.Write(ex);
            }
            finally
            {
                if (context != null)
                {
                    context.Dispose();
                }
            }

            return xsnURL;
        }

        /// <summary>
        /// Creates the form service proxy object.
        /// </summary>
        /// <param name="siteURL">The absolute URL of the top level SharePoint site.</param>
        private static void CreateFormServiceProxy(string siteURL)
        {
            LogToConsole("Creating forms service proxy using current logged in user credentials...", true);

            try
            {
                formsService = new FormsServicesWebService();
                formsService.Url = string.Format("{0}/_vti_bin/formsservices.asmx", siteURL.TrimEnd("/".ToCharArray()));
                formsService.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
            }
            catch (Exception ex)
            {
                LogToConsole(ex.Message, true);
                Console.WriteLine(ex);
            }
        }

        /// <summary>
        /// Validates the form template via the form service proxy.
        /// </summary>
        /// <param name="filearray">Form template byte array.</param>
        /// <returns>True: Valid form template. False: Invalid form template.</returns>
        private static bool IsValidFormTemplate(byte[] filearray)
        {
            try
            {
                LogToConsole("Validating the form template...", true);
                string base64string = System.Convert.ToBase64String(filearray, Base64FormattingOptions.None);
                CreateFormServiceProxy(rootSPSiteURL);
                DesignCheckerInformation designInfo = formsService.DesignCheckFormTemplate(1033, base64string, "InfoPath 14");
                Message errmsg = designInfo.Messages.FirstOrDefault(i => i.Id == 1 && i.Type == MessageType.Error);
                if (errmsg != null)
                {
                    LogToConsole(errmsg.ShortMessage, true);
                    throw new Exception(errmsg.ShortMessage);
                }
                else
                {
                    LogToConsole("The form template is valid...", true);
                    return true;
                }
            }
            catch (Exception ex)
            {
                LogToConsole("Error occured in validating the local form template file...", true);
                Console.Write(ex);
            }

            return false;
        }

        /// <summary>
        /// Converts the XSN template to browser enabled form.
        /// </summary>
        /// <param name="formURL">Absolute URL of the form template.</param>
        private static void ConvertFormToBrowserEnabled(string formURL)
        {
            if (formsService != null)
            {
                LogToConsole("Converting the form template to browser enabled...", true);

                try
                {
                    MessagesResponse msgRes = formsService.BrowserEnableUserFormTemplate(formURL);
                    if (msgRes != null)
                    {
                        if (msgRes.Messages.Count() > 0)
                        {
                            LogToConsole(msgRes.Messages[0].ShortMessage, true);
                            LogToConsole(msgRes.Messages[0].DetailedMessage, true);
                        }
                        else
                        {
                            LogToConsole("Successfully converted the form template to browser enabled form...", true);
                        }
                    }
                    else
                    {
                        LogToConsole("Error occured...", true);
                    }
                }
                catch (Exception ex)
                {
                    LogToConsole("Error occured in converting the form template to browser enabled...", true);
                    LogToConsole(ex.Message, true);
                }
            }
            else
            {
                LogToConsole("Error occured in creating the forms service proxy...", true);
            }
        }

        /// <summary>
        /// Converts a local XSN file to byte array.
        /// </summary>
        /// <param name="xsnPath">Local file path of XSN file.</param>
        /// <param name="fileName">Out parameter file name.</param>
        /// <returns>Byte array of the XSN file.</returns>
        private static byte[] ConvertFormToByteArray(string xsnPath, out string fileName)
        {
            byte[] filearray = null;
            fileName = string.Empty;

            try
            {
                LogToConsole("Reading the local form template from " + xsnPath, true);
                if (System.IO.File.Exists(xsnPath) && Path.GetExtension(xsnPath) == ".xsn")
                {
                    filearray = System.IO.File.ReadAllBytes(xsnPath);
                    fileName = Path.GetFileName(xsnPath);
                    if (filearray.Length > 0 && IsValidFormTemplate(filearray))
                    {
                        return filearray;
                    }
                }
                else
                {
                    LogToConsole("Form template does not exist on the specified path...", true);
                }
            }
            catch (Exception ex)
            {
                LogToConsole("Error occured in reading the local form template file...", true);
                Console.Write(ex);
            }

            return null;
        }

        /// <summary>
        /// Gets the URL of the top level SharePoint site.
        /// </summary>
        /// <param name="url">Complete URL as entered by the user.</param>
        /// <returns>Top level SharePoint site URL.</returns>
        private static string GetSiteURL(string url)
        {
            string spsiteURL = string.Empty;
            try
            {
                Uri uri = new Uri(url);
                if (uri != null)
                {
                    spsiteURL = uri.AbsoluteUri;
                    spsiteURL = spsiteURL.TrimEnd("/".ToCharArray()) + "/";
                }
            }
            catch (Exception ex)
            {
                Console.Write(ex);
            }

            return spsiteURL;
        }

        /// <summary>
        /// Logs a message to console.
        /// </summary>
        /// <param name="message">Message to be displayed.</param>
        private static void LogToConsole(string message)
        {
            Console.WriteLine(message);
        }

        /// <summary>
        /// Logs a message to console.
        /// </summary>
        /// <param name="message">Message to be displayed.</param>
        /// <param name="includetimestamp">Include the current timestamp.</param>
        private static void LogToConsole(string message, bool includetimestamp)
        {
            Console.WriteLine(message);
            using (StreamWriter sw = new StreamWriter(System.Diagnostics.Process.GetCurrentProcess().ProcessName + "_Log.txt", true))
            {
                if (includetimestamp)
                {
                    sw.WriteLine(string.Format("{1}      {0}", message, DateTime.Now.ToString()));
                }
                else
                {
                    sw.WriteLine(string.Format("{0}", message));
                }

                sw.Close();
            }
        }

        /// <summary>
        /// Gets the permission object mask.
        /// </summary>
        /// <returns>Permission object.</returns>
        private static BasePermissions GetPermissionMask()
        {
            BasePermissions bs = new BasePermissions();
            bs.Set(PermissionKind.FullMask);

            return bs;
        }

        #endregion
    }
}
